home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / gnucash / gnucash-2.6.5-setup.exe / {app} / bin / gtk-builder-convert < prev    next >
Text File  |  2014-11-03  |  29KB  |  800 lines

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2006-2008 Async Open Source
  4. #                         Henrique Romano <henrique@async.com.br>
  5. #                         Johan Dahlin <jdahlin@async.com.br>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU Lesser General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Lesser General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. #
  21. # TODO:
  22. #  Toolbars
  23.  
  24. """Usage: gtk-builder-convert [OPTION] [INPUT] [OUTPUT]
  25. Converts Glade files into XML files which can be loaded with GtkBuilder.
  26. The [INPUT] file is
  27.  
  28.   -w, --skip-windows     Convert everything but GtkWindow subclasses.
  29.   -r, --root             Convert only widget named root and its children
  30.   -h, --help             display this help and exit
  31.  
  32. When OUTPUT is -, write to standard output.
  33.  
  34. Examples:
  35.   gtk-builder-convert preference.glade preferences.ui
  36.  
  37. Report bugs to http://bugzilla.gnome.org/."""
  38.  
  39. import getopt
  40. import os
  41. import sys
  42.  
  43. from xml.dom import minidom, Node
  44.  
  45. DIALOGS = ['GtkDialog',
  46.            'GtkFileChooserDialog',
  47.            'GtkMessageDialog']
  48. WINDOWS = ['GtkWindow'] + DIALOGS
  49.  
  50. # The subprocess is only available in Python 2.4+
  51. try:
  52.     import subprocess
  53.     subprocess # pyflakes
  54. except ImportError:
  55.     subprocess = None
  56.  
  57. def get_child_nodes(node):
  58.     assert node.tagName == 'object'
  59.     nodes = []
  60.     for child in node.childNodes:
  61.         if child.nodeType != Node.ELEMENT_NODE:
  62.             continue
  63.         if child.tagName != 'child':
  64.             continue
  65.         nodes.append(child)
  66.     return nodes
  67.  
  68. def get_properties(node):
  69.     assert node.tagName == 'object'
  70.     properties = {}
  71.     for child in node.childNodes:
  72.         if child.nodeType != Node.ELEMENT_NODE:
  73.             continue
  74.         if child.tagName != 'property':
  75.             continue
  76.         value = child.childNodes[0].data
  77.         properties[child.getAttribute('name')] = value
  78.     return properties
  79.  
  80. def get_property(node, property_name):
  81.     assert node.tagName == 'object'
  82.     properties = get_properties(node)
  83.     return properties.get(property_name)
  84.  
  85. def get_property_node(node, property_name):
  86.     assert node.tagName == 'object'
  87.     properties = {}
  88.     for child in node.childNodes:
  89.         if child.nodeType != Node.ELEMENT_NODE:
  90.             continue
  91.         if child.tagName != 'property':
  92.             continue
  93.         if child.getAttribute('name') == property_name:
  94.             return child
  95.  
  96. def get_signal_nodes(node):
  97.     assert node.tagName == 'object'
  98.     signals = []
  99.     for child in node.childNodes:
  100.         if child.nodeType != Node.ELEMENT_NODE:
  101.             continue
  102.         if child.tagName == 'signal':
  103.             signals.append(child)
  104.     return signals
  105.  
  106. def get_property_nodes(node):
  107.     assert node.tagName == 'object'
  108.     properties = []
  109.     for child in node.childNodes:
  110.         if child.nodeType != Node.ELEMENT_NODE:
  111.             continue
  112.         # FIXME: handle comments
  113.         if child.tagName == 'property':
  114.             properties.append(child)
  115.     return properties
  116.  
  117. def get_accelerator_nodes(node):
  118.     assert node.tagName == 'object'
  119.     accelerators = []
  120.     for child in node.childNodes:
  121.         if child.nodeType != Node.ELEMENT_NODE:
  122.             continue
  123.         if child.tagName == 'accelerator':
  124.             accelerators.append(child)
  125.     return accelerators
  126.  
  127. def get_object_node(child_node):
  128.     assert child_node.tagName == 'child', child_node
  129.     nodes = []
  130.     for node in child_node.childNodes:
  131.         if node.nodeType != Node.ELEMENT_NODE:
  132.             continue
  133.         if node.tagName == 'object':
  134.             nodes.append(node)
  135.     assert len(nodes) == 1, nodes
  136.     return nodes[0]
  137.  
  138. def copy_properties(node, props, prop_dict):
  139.     assert node.tagName == 'object'
  140.     for prop_name in props:
  141.         child = get_property_node(node, prop_name)
  142.         if child is not None:
  143.             prop_dict[prop_name] = child
  144.  
  145.     return node
  146.  
  147. class GtkBuilderConverter(object):
  148.  
  149.     def __init__(self, skip_windows, target_version, root):
  150.         self.skip_windows = skip_windows
  151.         self.target_version = target_version
  152.         self.root = root
  153.         self.root_objects = []
  154.         self.objects = {}
  155.  
  156.     #
  157.     # Public API
  158.     #
  159.  
  160.     def parse_file(self, file):
  161.         self._dom = minidom.parse(file)
  162.         self._parse()
  163.  
  164.     def parse_buffer(self, buffer):
  165.         self._dom = minidom.parseString(buffer)
  166.         self._parse()
  167.  
  168.     def to_xml(self):
  169.         xml = self._dom.toprettyxml("", "")
  170.         return xml.encode('utf-8')
  171.  
  172.     #
  173.     # Private
  174.     #
  175.  
  176.     def _get_object(self, name):
  177.         return self.objects.get(name)
  178.  
  179.     def _get_objects_by_attr(self, attribute, value):
  180.         return [w for w in self._dom.getElementsByTagName("object")
  181.                       if w.getAttribute(attribute) == value]
  182.  
  183.     def _create_object(self, obj_class, obj_id, template=None, properties=None):
  184.         """
  185.         Creates a new <object> tag.
  186.         Optionally a name template can be provided which will be used
  187.         to avoid naming collisions.
  188.         The properties dictionary can either contain string values or Node
  189.         values. If a node is provided the name of the node will be overridden
  190.         by the dictionary key.
  191.  
  192.         @param obj_class: class of the object (class tag)
  193.         @param obj_id: identifier of the object (id tag)
  194.         @param template: name template to use, for example 'button'
  195.         @param properties: dictionary of properties
  196.         @type properties: string or Node.
  197.         @returns: Newly created node of the object
  198.         """
  199.         if template is not None:
  200.             count = 1
  201.             while True:
  202.                 obj_id = template + str(count)
  203.                 widget = self._get_object(obj_id)
  204.                 if widget is None:
  205.                     break
  206.  
  207.                 count += 1
  208.  
  209.         obj = self._dom.createElement('object')
  210.         obj.setAttribute('class', obj_class)
  211.         obj.setAttribute('id', obj_id)
  212.         if properties:
  213.             for name, value in properties.items():
  214.                 if isinstance(value, Node):
  215.                     # Reuse the node, so translatable and context still will be
  216.                     # set when converting nodes. See also #509153
  217.                     prop = value
  218.                 else:
  219.                     prop = self._dom.createElement('property')
  220.                     prop.appendChild(self._dom.createTextNode(value))
  221.  
  222.                 prop.setAttribute('name', str(name))
  223.                 obj.appendChild(prop)
  224.         self.objects[obj_id] = obj
  225.         return obj
  226.  
  227.     def _create_root_object(self, obj_class, template, properties=None):
  228.         obj = self._create_object(obj_class, None, template, properties)
  229.         self.root_objects.append(obj)
  230.         return obj
  231.  
  232.     def _parse(self):
  233.         glade_iface = self._dom.getElementsByTagName("glade-interface")
  234.         assert glade_iface, ("Badly formed XML, there is "
  235.                              "no <glade-interface> tag.")
  236.         # Rename glade-interface to interface
  237.         glade_iface[0].tagName = 'interface'
  238.         self._interface = glade_iface[0]
  239.  
  240.         # Remove glade-interface doc type
  241.         for node in self._dom.childNodes:
  242.             if node.nodeType == Node.DOCUMENT_TYPE_NODE:
  243.                 if node.name == 'glade-interface':
  244.                     self._dom.removeChild(node)
  245.  
  246.         # Strip unsupported tags
  247.         for tag in ['requires', 'requires-version']:
  248.             for child in self._dom.getElementsByTagName(tag):
  249.                 child.parentNode.removeChild(child)
  250.  
  251.         if self.root:
  252.             self._strip_root(self.root)
  253.  
  254.         # Rename widget to object
  255.         objects = self._dom.getElementsByTagName("widget")
  256.         for node in objects:
  257.             node.tagName = "object"
  258.  
  259.         for node in objects:
  260.             self._convert(node.getAttribute("class"), node)
  261.             if self._get_object(node.getAttribute('id')) is not None:
  262.         print "WARNING: duplicate id \"" + node.getAttribute('id') + "\""
  263.             self.objects[node.getAttribute('id')] = node
  264.  
  265.         # Convert Gazpachos UI tag
  266.         for node in self._dom.getElementsByTagName("ui"):
  267.             self._convert_ui(node)
  268.  
  269.         # Convert accessibility tag
  270.         for node in self._dom.getElementsByTagName("accessibility"):
  271.             self._convert_accessibility(node)
  272.  
  273.         # Output the newly created root objects and sort them
  274.         # by attribute id
  275.         # FIXME: Use sorted(self.root_objects,
  276.         #                   key=lambda n: n.getAttribute('id'),
  277.         #                   reverse=True):
  278.         # when we can depend on python 2.4 or higher
  279.         root_objects = self.root_objects[:]
  280.         root_objects.sort(lambda a, b: cmp(b.getAttribute('id'),
  281.                                            a.getAttribute('id')))
  282.         for obj in root_objects:
  283.             self._interface.childNodes.insert(0, obj)
  284.  
  285.     def _convert(self, klass, node):
  286.         if klass == 'GtkNotebook':
  287.             self._packing_prop_to_child_attr(node, "type", "tab")
  288.         elif klass in ['GtkExpander', 'GtkFrame']:
  289.             self._packing_prop_to_child_attr(
  290.                 node, "type", "label_item", "label")
  291.         elif klass == "GtkMenuBar":
  292.             self._convert_menu(node)
  293.         elif klass == "GtkMenu":
  294.             # Only convert toplevel popups
  295.             if node.parentNode == self._interface:
  296.                 self._convert_menu(node, popup=True)
  297.         elif klass in WINDOWS and self.skip_windows:
  298.             self._remove_window(node)
  299.  
  300.         if self.target_version == "3.0":
  301.             if klass == "GtkComboBoxEntry":
  302.                 node.setAttribute("class","GtkComboBox")
  303.                 prop = self._dom.createElement("property")
  304.                 prop.setAttribute("name", "has-entry")
  305.                 prop.appendChild(self._dom.createTextNode("True"))
  306.                 node.appendChild(prop)
  307.             elif klass == "GtkDialog":
  308.                 for child in node.childNodes:
  309.                     if child.nodeType != Node.ELEMENT_NODE:
  310.                         continue
  311.                     if child.tagName != 'property':
  312.                         continue
  313.                     if (child.getAttribute("name") not in ("has-separator", "has_separator")):
  314.                         continue;
  315.                     node.removeChild(child)
  316.                     break
  317.  
  318.         self._default_widget_converter(node)
  319.  
  320.     def _default_widget_converter(self, node):
  321.         klass = node.getAttribute("class")
  322.         for prop in get_property_nodes(node):
  323.             prop_name = prop.getAttribute("name")
  324.             if prop_name == "sizegroup":
  325.                 self._convert_sizegroup(node, prop)
  326.             elif prop_name == "tooltip" and klass != "GtkAction":
  327.                 prop.setAttribute("name", "tooltip-text")
  328.             elif prop_name in ["response_id", 'response-id']:
  329.                 # It does not make sense to convert responses when
  330.                 # we're not going to output dialogs
  331.                 if self.skip_windows:
  332.                     continue
  333.                 object_id = node.getAttribute('id')
  334.                 response = prop.childNodes[0].data
  335.                 self._convert_dialog_response(node, object_id, response)
  336.                 prop.parentNode.removeChild(prop)
  337.             elif prop_name == "adjustment":
  338.                 self._convert_adjustment(prop)
  339.             elif prop_name == "items" and klass in ['GtkComboBox',
  340.                                                     'GtkComboBoxEntry']:
  341.                 self._convert_combobox_items(node, prop)
  342.             elif prop_name == "text" and klass == 'GtkTextView':
  343.                 self._convert_textview_text(prop)
  344.  
  345.     def _remove_window(self, node):
  346.         object_node = get_object_node(get_child_nodes(node)[0])
  347.         parent = node.parentNode
  348.         parent.removeChild(node)
  349.         parent.appendChild(object_node)
  350.  
  351.     def _convert_menu(self, node, popup=False):
  352.         if node.hasAttribute('constructor'):
  353.             return
  354.  
  355.         uimgr = self._create_root_object('GtkUIManager',
  356.                                          template='uimanager')
  357.  
  358.         if popup:
  359.             name = 'popup'
  360.         else:
  361.             name = 'menubar'
  362.  
  363.         menu = self._dom.createElement(name)
  364.         menu.setAttribute('name', node.getAttribute('id'))
  365.         node.setAttribute('constructor', uimgr.getAttribute('id'))
  366.  
  367.         for child in get_child_nodes(node):
  368.             obj_node = get_object_node(child)
  369.             item = self._convert_menuitem(uimgr, obj_node)
  370.             menu.appendChild(item)
  371.             child.removeChild(obj_node)
  372.             child.parentNode.removeChild(child)
  373.  
  374.         ui = self._dom.createElement('ui')
  375.         uimgr.appendChild(ui)
  376.  
  377.         ui.appendChild(menu)
  378.  
  379.     def _convert_menuitem(self, uimgr, obj_node):
  380.         children = get_child_nodes(obj_node)
  381.         name = 'menuitem'
  382.         if children:
  383.             child_node = children[0]
  384.             menu_node = get_object_node(child_node)
  385.             # Can be GtkImage, which will take care of later.
  386.             if menu_node.getAttribute('class') == 'GtkMenu':
  387.                 name = 'menu'
  388.  
  389.         object_class = obj_node.getAttribute('class')
  390.         if object_class in ['GtkMenuItem',
  391.                             'GtkImageMenuItem',
  392.                             'GtkCheckMenuItem',
  393.                             'GtkRadioMenuItem']:
  394.             menu = self._dom.createElement(name)
  395.         elif object_class == 'GtkSeparatorMenuItem':
  396.             return self._dom.createElement('separator')
  397.         else:
  398.             raise NotImplementedError(object_class)
  399.  
  400.         menu.setAttribute('action', obj_node.getAttribute('id'))
  401.         self._add_action_from_menuitem(uimgr, obj_node)
  402.         if children:
  403.             for child in get_child_nodes(menu_node):
  404.                 obj_node = get_object_node(child)
  405.                 item = self._convert_menuitem(uimgr, obj_node)
  406.                 menu.appendChild(item)
  407.                 child.removeChild(obj_node)
  408.                 child.parentNode.removeChild(child)
  409.         return menu
  410.  
  411.     def _menuitem_to_action(self, node, properties):
  412.         copy_properties(node, ['label', 'tooltip'], properties)
  413.  
  414.     def _togglemenuitem_to_action(self, node, properties):
  415.         self._menuitem_to_action(node, properties)
  416.         copy_properties(node, ['active'], properties)
  417.  
  418.     def _radiomenuitem_to_action(self, node, properties):
  419.         self._togglemenuitem_to_action(node, properties)
  420.         copy_properties(node, ['group'], properties)
  421.  
  422.     def _add_action_from_menuitem(self, uimgr, node):
  423.         properties = {}
  424.         object_class = node.getAttribute('class')
  425.         object_id = node.getAttribute('id')
  426.         if object_class == 'GtkMenuItem':
  427.             name = 'GtkAction'
  428.             self._menuitem_to_action(node, properties)
  429.         elif object_class == 'GtkCheckMenuItem':
  430.             name = 'GtkToggleAction'
  431.             self._togglemenuitem_to_action(node, properties)
  432.         elif object_class == 'GtkRadioMenuItem':
  433.             name = 'GtkRadioAction'
  434.             self._radiomenuitem_to_action(node, properties)
  435.         elif object_class == 'GtkImageMenuItem':
  436.             name = 'GtkAction'
  437.             children = get_child_nodes(node)
  438.             if (children and
  439.                 children[0].getAttribute('internal-child') == 'image'):
  440.                 image = get_object_node(children[0])
  441.                 child = get_property_node(image, 'stock')
  442.                 if child is not None:
  443.                     properties['stock_id'] = child
  444.             self._menuitem_to_action(node, properties)
  445.         elif object_class == 'GtkSeparatorMenuItem':
  446.             return
  447.         else:
  448.             raise NotImplementedError(object_class)
  449.  
  450.         if get_property(node, 'use_stock') == 'True':
  451.             if 'label' in properties:
  452.                 properties['stock_id'] = properties['label']
  453.                 del properties['label']
  454.  
  455.         properties['name'] = object_id
  456.         action = self._create_object(name,
  457.                                      object_id,
  458.                                      properties=properties)
  459.         for signal in get_signal_nodes(node):
  460.             signal_name = signal.getAttribute('name')
  461.             if signal_name in ['activate', 'toggled']:
  462.                 action.appendChild(signal)
  463.             else:
  464.                 print 'Unhandled signal %s::%s' % (node.getAttribute('class'),
  465.                                                    signal_name)
  466.  
  467.         if not uimgr.childNodes:
  468.             child = self._dom.createElement('child')
  469.             uimgr.appendChild(child)
  470.  
  471.             group = self._create_object('GtkActionGroup', None,
  472.                                         template='actiongroup')
  473.             child.appendChild(group)
  474.         else:
  475.             group = uimgr.childNodes[0].childNodes[0]
  476.  
  477.         child = self._dom.createElement('child')
  478.         group.appendChild(child)
  479.         child.appendChild(action)
  480.  
  481.         for accelerator in get_accelerator_nodes(node):
  482.             signal_name = accelerator.getAttribute('signal')
  483.             if signal_name != 'activate':
  484.                 print 'Unhandled accelerator signal for %s::%s' % (
  485.                     node.getAttribute('class'), signal_name)
  486.                 continue
  487.             accelerator.removeAttribute('signal')
  488.             child.appendChild(accelerator)
  489.  
  490.     def _convert_sizegroup(self, node, prop):
  491.         # This is Gazpacho only
  492.         node.removeChild(prop)
  493.         obj = self._get_object(prop.childNodes[0].data)
  494.         if obj is None:
  495.             widgets = self._get_objects_by_attr("class", "GtkSizeGroup")
  496.             if widgets:
  497.                 obj = widgets[-1]
  498.             else:
  499.                 obj = self._create_root_object('GtkSizeGroup',
  500.                                                template='sizegroup')
  501.  
  502.         widgets = obj.getElementsByTagName("widgets")
  503.         if widgets:
  504.             assert len(widgets) == 1
  505.             widgets = widgets[0]
  506.         else:
  507.             widgets = self._dom.createElement("widgets")
  508.             obj.appendChild(widgets)
  509.  
  510.         member = self._dom.createElement("widget")
  511.         member.setAttribute("name", node.getAttribute("id"))
  512.         widgets.appendChild(member)
  513.  
  514.     def _convert_dialog_response(self, node, object_name, response):
  515.         # 1) Get parent dialog node
  516.         while True:
  517.             # If we can't find the parent dialog, give up
  518.             if node == self._dom:
  519.                 return
  520.  
  521.             if (node.tagName == 'object' and
  522.                 node.getAttribute('class') in DIALOGS):
  523.                 dialog = node
  524.                 break
  525.             node = node.parentNode
  526.             assert node
  527.  
  528.         # 2) Get dialogs action-widgets tag, create if not found
  529.         for child in dialog.childNodes:
  530.             if child.nodeType != Node.ELEMENT_NODE:
  531.                 continue
  532.             if child.tagName == 'action-widgets':
  533.                 actions = child
  534.                 break
  535.         else:
  536.             actions = self._dom.createElement("action-widgets")
  537.             dialog.appendChild(actions)
  538.  
  539.         # 3) Add action-widget tag for the response
  540.         action = self._dom.createElement("action-widget")
  541.         action.setAttribute("response", response)
  542.         action.appendChild(self._dom.createTextNode(object_name))
  543.         actions.appendChild(action)
  544.  
  545.     def _convert_adjustment(self, prop):
  546.         properties = {}
  547.         if prop.childNodes:
  548.             data = prop.childNodes[0].data
  549.             value, lower, upper, step, page, page_size = data.split(' ')
  550.             properties.update(value=value,
  551.                               lower=lower,
  552.                               upper=upper,
  553.                               step_increment=step,
  554.                               page_increment=page,
  555.                               page_size=page_size)
  556.         else:
  557.             prop.appendChild(self._dom.createTextNode(""))
  558.  
  559.         adj = self._create_root_object("GtkAdjustment",
  560.                                        template='adjustment',
  561.                                        properties=properties)
  562.         prop.childNodes[0].data = adj.getAttribute('id')
  563.  
  564.     def _convert_combobox_items(self, node, prop):
  565.         parent = prop.parentNode
  566.         if not prop.childNodes:
  567.             parent.removeChild(prop)
  568.             return
  569.  
  570.         translatable_attr = prop.attributes.get('translatable')
  571.         translatable = translatable_attr is not None and translatable_attr.value == 'yes'
  572.         has_context_attr = prop.attributes.get('context')
  573.         has_context = has_context_attr is not None and has_context_attr.value == 'yes'
  574.         comments_attr = prop.attributes.get('comments')
  575.         comments = comments_attr is not None and comments_attr.value or None
  576.  
  577.         value = prop.childNodes[0].data
  578.         model = self._create_root_object("GtkListStore",
  579.                                          template="model")
  580.  
  581.         columns = self._dom.createElement('columns')
  582.         model.appendChild(columns)
  583.  
  584.         column = self._dom.createElement('column')
  585.         column.setAttribute('type', 'gchararray')
  586.         columns.appendChild(column)
  587.  
  588.         data = self._dom.createElement('data')
  589.         model.appendChild(data)
  590.  
  591.         if value.endswith('\n'):
  592.             value = value[:-1]
  593.         for item in value.split('\n'):
  594.             row = self._dom.createElement('row')
  595.             data.appendChild(row)
  596.  
  597.             col = self._dom.createElement('col')
  598.             col.setAttribute('id', '0')
  599.             if translatable:
  600.                 col.setAttribute('translatable', 'yes')
  601.             if has_context:
  602.                 splitting = item.split('|', 1)
  603.                 if len(splitting) == 2:
  604.                     context, item = splitting
  605.                     col.setAttribute('context', context)
  606.             if comments is not None:
  607.                 col.setAttribute('comments', comments)
  608.             col.appendChild(self._dom.createTextNode(item))
  609.             row.appendChild(col)
  610.  
  611.         model_prop = self._dom.createElement('property')
  612.         model_prop.setAttribute('name', 'model')
  613.         model_prop.appendChild(
  614.             self._dom.createTextNode(model.getAttribute('id')))
  615.         parent.appendChild(model_prop)
  616.  
  617.         parent.removeChild(prop)
  618.  
  619.         child = self._dom.createElement('child')
  620.         node.appendChild(child)
  621.         cell_renderer = self._create_object('GtkCellRendererText', None,
  622.                                             template='renderer')
  623.         child.appendChild(cell_renderer)
  624.  
  625.         attributes = self._dom.createElement('attributes')
  626.         child.appendChild(attributes)
  627.  
  628.         attribute = self._dom.createElement('attribute')
  629.         attributes.appendChild(attribute)
  630.         attribute.setAttribute('name', 'text')
  631.         attribute.appendChild(self._dom.createTextNode('0'))
  632.  
  633.     def _convert_textview_text(self, prop):
  634.         if not prop.childNodes:
  635.             prop.parentNode.removeChild(prop)
  636.             return
  637.  
  638.         data = prop.childNodes[0].data
  639.         if prop.hasAttribute('translatable'):
  640.             prop.removeAttribute('translatable')
  641.         tbuffer = self._create_root_object("GtkTextBuffer",
  642.                                            template='textbuffer',
  643.                                            properties=dict(text=data))
  644.         prop.childNodes[0].data = tbuffer.getAttribute('id')
  645.         prop.setAttribute('name', 'buffer')
  646.  
  647.     def _packing_prop_to_child_attr(self, node, prop_name, prop_val,
  648.                                    attr_val=None):
  649.         for child in get_child_nodes(node):
  650.             packing_props = [p for p in child.childNodes if p.nodeName == "packing"]
  651.             if not packing_props:
  652.                 continue
  653.             assert len(packing_props) == 1
  654.             packing_prop = packing_props[0]
  655.             properties = packing_prop.getElementsByTagName("property")
  656.             for prop in properties:
  657.                 if (prop.getAttribute("name") != prop_name or
  658.                     prop.childNodes[0].data != prop_val):
  659.                     continue
  660.                 packing_prop.removeChild(prop)
  661.                 child.setAttribute(prop_name, attr_val or prop_val)
  662.             if len(properties) == 1:
  663.                 child.removeChild(packing_prop)
  664.  
  665.     def _convert_ui(self, node):
  666.         cdata = node.childNodes[0]
  667.         data = cdata.toxml().strip()
  668.         if not data.startswith("<![CDATA[") or not data.endswith("]]>"):
  669.             return
  670.         data = data[9:-3]
  671.         child = minidom.parseString(data).childNodes[0]
  672.         nodes = child.childNodes[:]
  673.         for child_node in nodes:
  674.             node.appendChild(child_node)
  675.         node.removeChild(cdata)
  676.         if not node.hasAttribute("id"):
  677.             return
  678.  
  679.         # Updating references made by widgets
  680.         parent_id = node.parentNode.getAttribute("id")
  681.         for widget in self._get_objects_by_attr("constructor",
  682.                                                 node.getAttribute("id")):
  683.             widget.getAttributeNode("constructor").value = parent_id
  684.         node.removeAttribute("id")
  685.  
  686.     def _convert_accessibility(self, node):
  687.         objectNode = node.parentNode
  688.         parent_id = objectNode.getAttribute("id")
  689.  
  690.         properties = {}
  691.         for node in node.childNodes:
  692.             if node.nodeName == 'atkproperty':
  693.                 node.tagName = 'property'
  694.                 properties[node.getAttribute('name')] = node
  695.                 node.parentNode.removeChild(node)
  696.             elif node.nodeName == 'atkrelation':
  697.                 node.tagName = 'relation'
  698.                 relation_type = node.getAttribute('type')
  699.                 relation_type = relation_type.replace('_', '-')
  700.                 node.setAttribute('type', relation_type)
  701.             elif node.nodeName == 'atkaction':
  702.                 node.tagName = 'action'
  703.  
  704.         if properties:
  705.             child = self._dom.createElement('child')
  706.             child.setAttribute("internal-child", "accessible")
  707.  
  708.             atkobject = self._create_object(
  709.                 "AtkObject", None,
  710.                 template='a11y-%s' % (parent_id,),
  711.                 properties=properties)
  712.             child.appendChild(atkobject)
  713.             objectNode.appendChild(child)
  714.  
  715.     def _strip_root(self, root_name):
  716.         for widget in self._dom.getElementsByTagName("widget"):
  717.             if widget.getAttribute('id') == root_name:
  718.                 break
  719.         else:
  720.             raise SystemExit("Could not find an object called `%s'" % (
  721.                 root_name))
  722.  
  723.         for child in self._interface.childNodes[:]:
  724.             if child.nodeType != Node.ELEMENT_NODE:
  725.                 continue
  726.             child.parentNode.removeChild(child)
  727.  
  728.         self._interface.appendChild(widget)
  729.  
  730.  
  731. def _indent(output):
  732.     if not subprocess:
  733.         return output
  734.  
  735.     for directory in os.environ['PATH'].split(os.pathsep):
  736.         filename = os.path.join(directory, 'xmllint')
  737.         if os.path.exists(filename):
  738.             break
  739.     else:
  740.         return output
  741.  
  742.     s = subprocess.Popen([filename, '--format', '-'],
  743.                          stdin=subprocess.PIPE,
  744.                          stdout=subprocess.PIPE)
  745.     s.stdin.write(output)
  746.     s.stdin.close()
  747.     return s.stdout.read()
  748.  
  749. def usage():
  750.     print __doc__
  751.  
  752. def main(args):
  753.     try:
  754.         opts, args = getopt.getopt(args[1:], "hwr:",
  755.                                    ["help",
  756.                                     "skip-windows",
  757.                                     "target-version=",
  758.                                     "root="])
  759.     except getopt.GetoptError:
  760.         usage()
  761.         return 2
  762.  
  763.     if len(args) != 2:
  764.         usage()
  765.         return 2
  766.  
  767.     input_filename, output_filename = args
  768.  
  769.     skip_windows = False
  770.     split = False
  771.     root = None
  772.     target_version = "2.0"
  773.     for o, a in opts:
  774.         if o in ("-h", "--help"):
  775.             usage()
  776.             sys.exit()
  777.         elif o in ("-r", "--root"):
  778.             root = a
  779.         elif o in ("-w", "--skip-windows"):
  780.             skip_windows = True
  781.         elif o in ("-t", "--target-version"):
  782.             target_version = a
  783.  
  784.     conv = GtkBuilderConverter(skip_windows=skip_windows,
  785.                                target_version=target_version,
  786.                                root=root)
  787.     conv.parse_file(input_filename)
  788.  
  789.     xml = _indent(conv.to_xml())
  790.     if output_filename == "-":
  791.         print xml
  792.     else:
  793.         open(output_filename, 'w').write(xml)
  794.         print "Wrote", output_filename
  795.  
  796.     return 0
  797.  
  798. if __name__ == "__main__":
  799.     sys.exit(main(sys.argv))
  800.